import time

from framework.rest.restapibase import RestAPIBase


class HeadSpinAPI(RestAPIBase):

    def __init__(self, config):
        """Initializes object

        :param config: config to read configuration from (dict)
        """

        super().__init__(config['headspin']['base_url'])
        self.config = config
        self.token = config['headspin']['api_key']
        self.headers = {'Authorization': f'Bearer {self.token}'}

    def get_device_local_config_by_udid(self, udid):
        devices = self.config['mobileapp']['headspin']['devices']
        for device in devices:
            if device['udid'] == udid:
                return device
        raise ValueError(f'Can not find the device {udid} from the local config')

    def get_device_local_config_by_name(self, device_name, ignore_unavailable=False, timeout=90, polling=10):
        devices = self.config['mobileapp']['headspin']['devices']
        end = time.time() + timeout
        if device_name.lower() in ['ios', 'android']:
            device_lst = [device for device in devices if device['platformName'].lower() == device_name.lower()]
            while device_lst is not None and len(device_lst) > 0 and time.time() <= end:
                for device in device_lst:
                    udid = device['udid']
                    if self.is_device_available(udid):
                        return device

                self.logger.info(f'All {device_name} devices are unavailable now, wait {polling} seconds!')
                time.sleep(polling)
        else:
            for device in devices:
                if device['deviceName'] == device_name:
                    if not ignore_unavailable:
                        end = time.time() + timeout
                        while not self.is_device_available(device['udid']) and time.time() <= end:
                            self.logger.info(f'The device {device["udid"]} is unavailable now'
                                             f', waiting {polling} seconds!')
                            time.sleep(polling)

                        if self.is_device_available(device['udid']):
                            return device
                        else:
                            raise ValueError(f'The device {device["udid"]} is unavailable now!')

                    else:
                        return device

        raise ValueError(f'Can not find the device {device_name} from the local config')

    def get_browser_local_config_by_name(self, browser_name):

        browsers = self.config['webapp']['headspin']['browseres']
        for browser in browsers:
            if browser['deviceName'] == browser_name:
                return browser

        raise ValueError(f'Can not find the browser {browser_name} from the local config')

    def get_device_udid_by_name(self, device_name, ignore_unavailable=False):

        device = self.get_device_local_config_by_name(device_name, ignore_unavailable=ignore_unavailable)
        return device['udid']

    def get_browser_udid_by_name(self, browser_name):

        browser = self.get_browser_local_config_by_name(browser_name)
        return browser['udid']

    def get_appId_by_BuildCommit(self, BuildCommit, raise_exception=False):

        apps = self.list_uploaded_app().json()['apps']
        for app in apps:
            if app['build_commit'] == BuildCommit:
                return app['app_id']
        if raise_exception:
            raise ValueError(f'Can not found appId by build_comment = {BuildCommit}')
        else:
            self.logger.info(f'The app not found by {BuildCommit}')
            return ''

    def list_uploaded_app(self):

        full_url = f'{self.base_url}/v1/apps'
        return self.Get(url=full_url, headers=self.headers)

    def install_pre_upload_app(self, device_id, app_id, sign=None):

        full_url = f'{self.base_url}/v1/app/{app_id}/install/{device_id}'
        params = {'sign': sign} if sign is not None else None
        return self.Post(url=full_url, headers=self.headers, params=params)

    def uninstall_app(self, device_Id, app_identifier, is_android):

        if is_android:
            return self._uninstall_app_android(device_Id, app_identifier)
        else:
            return self._uninstall_app_ios(device_Id, app_identifier)

    def _uninstall_app_android(self, device_id, package_name):

        full_url = f'{self.base_url}/v0/adb/{device_id}/uninstall'
        params = {'package': package_name}
        return self.Post(url=full_url, headers=self.headers, params=params)

    def _uninstall_app_ios(self, device_id, bundle_id):

        full_url = f'{self.base_url}/v0/idevice/{device_id}/installer/uninstall'
        params = {'appid': bundle_id}
        return self.Post(url=full_url, headers=self.headers, params=params)

    def upload_app(self, file_fullpath, build_commit):

        full_url = f'{self.base_url}/v1/app/upload'
        files = {
            'app': open(file_fullpath, 'rb')
            , 'metadata': (None, f'{{"build_commit": "{build_commit}"}}')
        }
        return self.Post(url=full_url, files=files, headers=self.headers)

    def is_device_available(self, device_id):

        # 1. 检查设备ID是否存在
        available_devices_lst = [i['device_id'] for i in self.get_device_url_list().json()['devices']]
        if device_id not in available_devices_lst:
            return False

        # 2. 检查设备是否被占用，当设备被占用是owner_email字段不为空
        all_devices_lst = self.get_device_list().json()['devices']
        device_and_owner_lst = [{i['device_id']: i['owner_email']} for i in all_devices_lst]
        device_used_lst = []
        for i in device_and_owner_lst:
            for key in i:
                if i[key]:
                    device_used_lst.append(key)
        return device_id not in device_used_lst

    def get_device_url_list(self):

        full_url = f'{self.base_url}/v0/device-url'
        return self.Get(full_url, headers=self.headers)

    def get_device_list(self):

        url = 'v0/devices'
        full_url = f'{self.base_url}/{url}'
        return self.Get(full_url, headers=self.headers)

    def get_driver_url_for_device(self, udid):

        self.logger.debug(f'Getting driver URL for device udid {udid}')
        return self._make_usable_url(self.get_device_info_from_automation_config(udid)
                                     ['driver_url'])

    def _make_usable_url(self, unusable_url):

        url_parts = unusable_url.split('{api_token}')
        url = f'{url_parts[0]}{self.token}{url_parts[1]}'
        return url

    def get_device_info_from_automation_config(self, udid):

        self.logger.debug(f'Getting device info for device udid {udid} from automation config')
        response = self.get_automation_config()

        for k, v in response.json().items():
            if k.split('@')[0] == udid:
                return v
        else:
            raise ValueError(f'Udid {udid} was not found')

    def get_automation_config(self):

        self.logger.debug('Getting automation config')
        url = 'v0/devices/automation-config'
        full_url = f'{self.base_url}/{url}'
        return self.Get(full_url, headers=self.headers)

    def get_device_capabilities(self, udid):

        self.logger.debug(f'Getting device capabilities for device udid {udid}')
        response = self.get_device_info_from_automation_config(udid)
        return response['capabilities']

    def lock_device(self, device_id, idle_timeout=60):

        url = f'v0/devices/lock'
        full_url = f'{self.base_url}/{url}'
        params = {'idleTimeout': idle_timeout}
        json = {'device_id': device_id}
        return self.Post(full_url, headers=self.headers, params=params, json=json)

    def unlock_device(self, device_id):

        url = f'v0/devices/unlock'
        full_url = f'{self.base_url}/{url}'
        json = {'device_id': device_id}
        return self.Post(full_url, headers=self.headers, json=json)

    def list_upload_app(self):

        full_url = f'{self.base_url}/v1/apps'
        return self.Get(url=full_url, headers=self.headers)

    def del_pre_upload_app(self, app_id):

        full_url = f'{self.base_url}/v1/app/{app_id}/delete'
        return self.Delete(url=full_url, headers=self.headers)

    def clear_upload_app_by_identifier(self, app_identifier, raise_exception=False):

        # 1. List all uploaded apps
        response = self.list_upload_app()
        if response.status_code != 200:
            raise ValueError(f'The response code should == 200, but actually it is {response.status_code}')

        if 'apps' in list(response.json().keys()):
            apps = response.json()['apps']
            for app in apps:
                if app['app_identifier'] == app_identifier:
                    app_id = app['app_id']
                    response = self.del_pre_upload_app(app_id)
                    if response.status_code != 200:
                        if raise_exception:
                            raise ValueError(f'Del package {app_identifier} failed!')
                        else:
                            return False
        return True
